#include "unreliable_layer.h"
#include "common_types.h"

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/uniform_01.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>

class UnreliableLayerImp : public boost::enable_shared_from_this<UnreliableLayerImp>
{
	double lostRate_;
	unsigned int avgDelay_;
	unsigned int delayDelta_;

	boost::variate_generator<boost::mt19937&, boost::normal_distribution<double> >* delayDist_;
	boost::variate_generator<boost::mt19937&, boost::uniform_01<double> >* lostDist_;

	boost::asio::io_service& service_;

	static boost::mt19937 RandomGeneratorEngine;

	boost::shared_ptr<IPackageReceiver> deliever_;

	void deliever(boost::asio::deadline_timer* timer, const udp::endpoint& remoteAddr, IPackage::Ptr package, IPackageSender::Ptr sender, unsigned int packageSeq)
	{
		delete timer;
		deliever_->handle(remoteAddr, package, sender, packageSeq);
	}

public:
	inline void handle(const udp::endpoint& remoteAddr, IPackage::Ptr package, IPackageSender::Ptr sender, unsigned int packageSeq)
	{
		if(lostDist_ == NULL || delayDist_ == NULL)
		{
			deliever_->handle(remoteAddr, package, sender, packageSeq);
			return;
		}
		auto v = (*lostDist_)();
		if(v < lostRate_)
		{
			return;
		}
		v = (*delayDist_)();
		if(v < 0)
		{
			v = avgDelay_ / 2.0;
		}

		auto timer = new boost::asio::deadline_timer(service_);
		timer->expires_from_now(boost::posix_time::millisec((__int64)v));
		timer->async_wait(boost::bind(&UnreliableLayerImp::deliever, shared_from_this(), timer, remoteAddr, package, sender, packageSeq));
	}
	
	inline void config(double lostRate, unsigned int avgDelay, unsigned int delayDelta)
	{
		lostRate_ = lostRate;
		avgDelay_ = avgDelay;
		delayDelta_ = delayDelta;

		if(lostDist_ != NULL)
		{
			delete lostDist_;
		}
		if(delayDist_ != NULL)
		{
			delete delayDist_;
		}
		delayDist_ = new boost::variate_generator<boost::mt19937&, boost::normal_distribution<double> >(RandomGeneratorEngine, boost::normal_distribution<double>(avgDelay_, delayDelta_));
		lostDist_ = new boost::variate_generator<boost::mt19937&, boost::uniform_01<double> >(RandomGeneratorEngine, boost::uniform_01<double>());
	}

	UnreliableLayerImp(boost::shared_ptr<IPackageReceiver> packageHandler, boost::asio::io_service& service): deliever_(packageHandler), service_(service),
		lostRate_(0), avgDelay_(0), delayDelta_(0), delayDist_(NULL), lostDist_(NULL)
	{

	}

	virtual ~UnreliableLayerImp()
	{
		if(delayDist_ != NULL)
		{
			delete delayDist_;
		}
		if(lostDist_ != NULL)
		{
			delete lostDist_;
		}
	}
};


UnreliableLayer::UnreliableLayer(boost::shared_ptr<IPackageReceiver> packageHandler, boost::asio::io_service& service) : imp_(new UnreliableLayerImp(packageHandler, service))
{
}

void UnreliableLayer::config(double lostRate, unsigned int avgDelay, unsigned int delayDelta)
{
	imp_->config(lostRate, avgDelay, delayDelta);
}

void UnreliableLayer::handle(const udp::endpoint& remoteAddr, IPackage::Ptr package, IPackageSender::Ptr sender, unsigned int packageSeq)
{
	imp_->handle(remoteAddr, package, sender, packageSeq);
}

UnreliableLayer::~UnreliableLayer()
{
}

boost::mt19937 UnreliableLayerImp::RandomGeneratorEngine;
